package com.icow.wms.basic.controller;

import com.alibaba.fastjson.JSON;
import com.icow.common.utils.DateUtils;
import com.icow.common.utils.StringUtils;
import com.icow.system.domain.SysOperLog;
import com.icow.system.mapper.SysConfigMapper;
import com.icow.system.mapper.SysOperLogMapper;
import com.icow.wcs.dao.OperatingPointDao;
import com.icow.wcs.dao.OrderDao;
import com.icow.wcs.dao.ShelfDao;
import com.icow.wcs.domain.*;
import com.icow.wcs.obj.LoadStatusEnum;
import com.icow.wcs.util.Result;
import com.icow.wms.basic.domain.WmsMatList;
import com.icow.wms.basic.mapper.WmsMatListMapper;
import com.icow.wms.basic.service.IWmsMatListService;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

import static java.util.stream.Collectors.toList;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
import static org.springframework.util.StringUtils.isEmpty;

@SuppressWarnings("all")
@Controller
@RequestMapping(value = {"/wms.basic/shelf", "/api/shelf"})
public class WmsController {
    public static final String IN_SHELF_PREFIX = "in_shelf_";
    public static final String OUT_SHELF_PREFIX = "out_shelf_";
    public static final String TRANSFER_SHELF_PREFIX = "transfer_shelf_";
    public static final String CONTINUE_SHELF_PREFIX = "continue_";
    public static final String STT_RETURN_SHELF_PREFIX = "stt_return_shelf_";
    public static final String STT_OUT_SHELF_PREFIX = "stt_out_shelf_";
    public static final String CALL_XIANBIAN_SHELF_PREFIX = "call_xian_bian";
    public static final String EMPTY_XIANBIAN_SHELF_PREFIX = "empty_xian_bian";
    @Autowired
    private ShelfDao shelfDao;
    @Autowired
    private OperatingPointDao operatingPointDao;
    @Autowired
    OrderDao orderDao;

    @Autowired
    private SysConfigMapper configMapper;

    @Autowired
    private SysOperLogMapper operLogMapper;

    @Autowired
    private WmsMatListMapper wmsMatListMapper;

    @Autowired
    private IWmsMatListService wmsMatListService;
//    @Autowired
//    ProductDao productDao;

    @RequestMapping("/index")
    String index() {
        return "shelf_index";
    }

    @Data
    public class Option {
        private String name;
        private boolean disabled;

        public Option(String name, boolean disabled) {
            super();
            this.name = name;
            this.disabled = disabled;
        }

    }

    @RequestMapping("/save")
    @ResponseBody
    Result save(ShelfDomain param) {
        String locationName = param.getLocationName();
        Integer id = param.getId();
        if (id == null && isEmpty(locationName)) {
            return Result.error("点位必填");
        }

        if (id == null) {
            param.setLoadStatus(LoadStatusEnum.EMPTY.status);

            ShelfDomain find = shelfDao.findByLocationNameAndLevel(locationName, param.getLevel());
            if (find != null) {
                return Result.error("保存失败：点位+层数已存在");
            }

            if (param.getCreatedDate() == null) {
                param.setCreatedDate(new Date());
            }
            if (param.getModifiedDate() == null) {
                param.setModifiedDate(new Date());
            }

            shelfDao.save(param);
        } else {
            ShelfDomain sd = shelfDao.getOne(id);
            Integer loadStatus = param.getLoadStatus();
            if (LoadStatusEnum.EMPTY.status.equals(loadStatus)) {
                //清空了货物
                sd.setPalletNo(null);
                sd.setBoxes(null);
                sd.setLoadStatus(LoadStatusEnum.EMPTY.status);
            } else {
                sd.setPalletNo(param.getPalletNo());
                //TODO boxes
                sd.setLoadStatus(loadStatus);
            }
            sd.setWarehouseNo(param.getWarehouseNo());
            sd.setShelfType(param.getShelfType());

            if (sd.getCreatedDate() == null) {
                sd.setCreatedDate(new Date());
            }
            if (param.getModifiedDate() == null) {
                param.setModifiedDate(new Date());
            }

            shelfDao.save(sd);
        }

//        int level = domain.getLevel();
//		if (isEmpty(locationAndLevel)) {
//			return Result.error("至少选择一个点位");
//		}
//		ShelfDomain s = shelfDao.findByNo(domain.getNo());
//		if (domain.getId() == null && s != null) {
//			return Result.error("序号不可重复");
//		}
//		s = shelfDao.findByName(domain.getName());
//		if (domain.getId() == null && s != null) {
//			return Result.error("名称不可重复");
//		}

        return Result.success();
    }

    @Data
    class ShelfQueryParam {
        private Integer start = 0;
        private Integer length = 10;
        private String warehouseNo;
        private String locationName;
        private String palletNo;
        private String boxNo;
        private Integer productId;
        //        private String productName;
        private String productNo;
    }

    @RequestMapping("/list")
    @ResponseBody
    PageResult<ShelfDomain> list(ShelfQueryParam param) {
        PageRequest pr = PageRequest.of(param.getStart() / param.getLength(), param.getLength());
        Specification<ShelfDomain> spec = Specification.where((root, query, builder) -> {
            List<Predicate> list = new ArrayList<>();
            if (isNotEmpty(param.getWarehouseNo())) {
                list.add(builder.equal(root.get("warehouseNo"), param.getWarehouseNo()));
            }
            if (isNotEmpty(param.getLocationName())) {
                list.add(builder.equal(root.get("locationName"), param.getLocationName()));
            }
//            if (isNotEmpty(param.getProductName())) {
//                list.add(builder.equal(root.get("productName"), param.getProductName()));
//            }
//            if (isNotEmpty(param.getProductNo())) {
//                list.add(builder.equal(root.get("productNo"), param.getProductNo()));
//            }

            if (isNotEmpty(param.getPalletNo())) {
                list.add(builder.equal(root.get("palletNo"), param.getPalletNo()));
            }
            Predicate[] p = new Predicate[list.size()];
            return builder.and(list.toArray(p));
        });
        Page<ShelfDomain> page = shelfDao.findAll(spec, pr);
        String boxNo = param.getBoxNo();
        String productNo = param.getProductNo();
        Integer productId = param.getProductId();
        // boxNo查询
        if (isNotEmpty(boxNo) || isNotEmpty(productNo) || productId != null) {
            List<ShelfDomain> list = page.getContent().stream().filter(it -> {
                if (it.getBoxes() == null) {
                    return false;
                }
                return it.getBoxes().stream().anyMatch(e -> (isNotEmpty(boxNo) && e.getBoxNo().equals(boxNo))
                        || (isNotEmpty(productNo) && e.getProductNo().equals(productNo))
                        || (productId != null && productId.equals(e.getProductId())));
            }).collect(toList());
            return new PageResult<>(list, page.getTotalElements());
        }

        return new PageResult<>(page);
    }

    @RequestMapping("/allEmptyShelf")
    @ResponseBody
    Result allEmptyShelf() {
        List<ShelfDomain> list = shelfDao.findByLoadStatusAndLockedIsNull(LoadStatusEnum.EMPTY.status);
        return Result.success(list);
    }

    @RequestMapping("/allOutShelf")
    @ResponseBody
    Result allOutShelf() {
        List<ShelfDomain> list = shelfDao.findByLoadStatusAndLockedIsNull(LoadStatusEnum.OUTSHELT.status);
        return Result.success(list);
    }

    @RequestMapping("/availableInShelf")
    @ResponseBody
    Result availableInShelf() {
        List<ShelfDomain> list = shelfDao.findByLoadStatusAndLockedIsNull(LoadStatusEnum.EMPTY.status);
        List<ShelfDomain> list2 = shelfDao.findByLoadStatusAndLockedIsNull(LoadStatusEnum.NON_EMPTY.status);
        list.addAll(list2);
        return Result.success(list);
    }

    @RequestMapping("/allFullShelf")
    @ResponseBody
    Result allFullShelf() {
        List<ShelfDomain> list = shelfDao.findByLoadStatusAndLockedIsNull(LoadStatusEnum.NON_EMPTY.status);
        return Result.success(list);
    }

    @RequestMapping("/allSttOutShelf")
    @ResponseBody
    Result allSttOutShelf() {
        List<ShelfDomain> list = shelfDao.findByLoadStatusAndLockedIsNull(LoadStatusEnum.STTOUTSHELT.status);
        return Result.success(list);
    }

    @RequestMapping("/delete")
    @ResponseBody
    Result delete(Integer id) {
        shelfDao.deleteById(id);
        return Result.success();
    }

    @RequestMapping("/toggleLock")
    @ResponseBody
    Result toggleLock(Integer id) {
        ShelfDomain sd = shelfDao.getOne(id);
        Boolean locked = sd.getLocked();
        System.out.println("toggleLock start=======>" + locked);
        sd.setLocked(Boolean.TRUE.equals(locked) ? null : true);
        System.out.println("toggleLock end=======>" + sd.getLocked());
        shelfDao.save(sd);
        return Result.success();
    }

    // --------------入库----------------

    @Data
    static class InShelfParam {
        private Integer type; // 1 托盘入库 2 料箱入库 3 货物入库
        @Nullable
        private Integer pickupPointId;// 入库点
        @Nullable
        private Integer intendShelfId;// 存放托盘

        private String locationName;// 入库点名称
        //private Integer productId;
        //private String productNo;
        private String palletNo;

        public void setPalletNo(String palletNo) {
            this.palletNo = "".equals(palletNo) ? null : palletNo;
        }

        private List<Box> boxes;

        private String noticeId;//任务号
    }

    @RequestMapping("/intest")
    @ResponseBody
    Result intest(@RequestBody InShelfParam param, HttpServletRequest request) {
        Map<String, String[]> m = request.getParameterMap();
        System.out.println(JSON.toJSONString(m));
        System.out.println(param);
        return Result.success();
    }

    // --------------出库----------------

    @Data
    static class OutShelfParam {
        private Integer outShelfId;  //出库点
        private Integer type;

        private String locationName;// 出库点名称

        private Integer intendShelfId;  //货位
        private String palletNo;
        private List<WmsShelfContent> boxes;
        private Integer backType; // 出库完后续操作，1回原货位，2去入库点，只对出库类型为3，4的有效
        private String noticeId;//任务号
    }

    @Data
    static class WmsShelfContent {
        private String boxNo;
        private Integer productId;
        private String productNo;
        private double productCount;
    }


    /**
     * 按需出库，并更新货位内容
     *
     * @param box 需要出库的货物和数量，此处数量可以大于4
     * @return 选中的货位，为空代表数量不够
     */
    private Set<ShelfDomain> chooseShelfs(WmsShelfContent box) {
        List<ShelfDomain> fullShelfs = shelfDao.findByLoadStatusAndLockedIsNull(LoadStatusEnum.NON_EMPTY.status);
        String boxNo = box.getBoxNo();
        Integer productId = box.getProductId();
        String productNo = box.getProductNo();
        double needCount = box.getProductCount();


        Set<ShelfDomain> chosenShelfs = new HashSet<>();
        boolean isEnough = false;
        double tempCount = 0;

        for (ShelfDomain s : fullShelfs) {
            if (s.getBoxes() == null) {
                continue;
            }
            ListIterator<Box> it = s.getBoxes().listIterator();
            while (it.hasNext()) {
                Box b = it.next();
                if (b == null) {
                    continue;
                }
                if (boxNo != null && boxNo.equals(b.getBoxNo())) {
                    isEnough = true;
                    it.set(null);
                    chosenShelfs.add(s);
                    break;
                }
                if (b.getProductId() != null && b.getProductId().equals(productId)
                        || productNo != null && productNo.equals(b.getProductNo())
                ) {
                    chosenShelfs.add(s);
                    tempCount += b.getProductCount();
                    if (tempCount >= needCount) {
                        if (tempCount == needCount) {
                            it.set(null);
                        } else {
                            b.setProductCount(tempCount - needCount);
                        }
                        isEnough = true;
                        break;
                    } else {
                        it.set(null);
                    }

                }
            }
            if (isEnough) {
                break;
            }
        }
        if (isEnough) {
            shelfDao.saveAll(fullShelfs);
            return chosenShelfs;
        }
        return Collections.emptySet();
    }


    // --------------移库----------------
    @Data
    static class TransferShelfParam {
        Integer sourceShelfId;
        Integer targetShelfId;
        String sourceShelfCode;
        String targetShelfCode;
    }


    @Data
    class MyTransferShelfParam {
        Integer pickupPointId;// 入库点
        Integer outShelfId;
    }


    @Data
    static class ShelfVo {
        private String locationName;
        private int level;// 层数，以0开始
        private Integer productId;
        private String productName;
        private String productNo;
        private String palletNo;

        public static ShelfVo from(ShelfDomain domain) {
            ShelfVo vo = new ShelfVo();
            vo.locationName = domain.getLocationName();
            vo.level = domain.getLevel();
            //TODO boxes
            vo.palletNo = domain.getPalletNo();
            return vo;
        }
    }


    // --------------盘点----------------

    @Data
    static class SttShelfParam {
        private Integer outShelfId;  //出库点
        private Integer type;
        private String locationName;
        private Integer intendShelfId;  //货位
        private String shelfCode; //货位编号
        private String palletNo;
        private List<WmsShelfContent> boxes;
        private Integer backType; // 出库完后续操作，1回原货位，2去入库点，只对出库类型为3，4的有效
        private String noticeId;//任务号
    }

    @Data
    static class WmsSttShelfContent {
        private String boxNo;
        private Integer productId;
        private String productNo;
        private int productCount;
    }

    @Data
    static class SttFinishShelfParam {
        private Integer outShelfId;  //出库点
        private String locationName;
        private Integer intendShelfId;
        private String shelfCode; //货位编号
        private String palletNo;
        private List<WmsShelfContent> boxes;
    }

    /**
     * POST 方法 JSON
     */
    private String rtPostJsonObject(String url, String param) {
        // 请求超时
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setConnectTimeout(5000);
        requestFactory.setReadTimeout(5000);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        HttpEntity<String> request = new HttpEntity<>(param, headers);
        RestTemplate restTemplate = new RestTemplate(requestFactory);
        ResponseEntity<String> response = null;
        try {
            response = restTemplate.postForEntity(url, request, String.class);
        } catch (Exception ex) {
            return ex.getMessage();
        }
        System.out.println(response.getBody());
        return response.getBody();
    }

    /**
     * 记录交互日志
     *
     * @param title
     * @param param
     * @param result
     */
    private void addSysOperLog(String title, String param, String result) {
        //记录交互日志
        SysOperLog logStart = new SysOperLog();
        logStart.setTitle(title);
        logStart.setBusinessType(0);
        logStart.setMethod("com.icow.wms.pro.controller.WcsController.orderChange");
        logStart.setRequestMethod("POST");
        logStart.setOperatorType(1);
        logStart.setOperName("system");
        logStart.setOperUrl("/api/wcs/orderChange");
        logStart.setOperIp("127.0.0.1");
        logStart.setOperLocation("内网ID");
        logStart.setOperParam(param);
        logStart.setJsonResult(result);
        logStart.setStatus(0);
        logStart.setOperTime(DateUtils.getNowDate());
        operLogMapper.insertOperlog(logStart);
    }

    /**
     * 记录交互日志
     *
     * @param title
     * @param param
     * @param result
     */
    private void addSysOperLog(String title, String param, String result, String method, String url, String operName) {
        //记录交互日志
        SysOperLog logStart = new SysOperLog();
        logStart.setTitle(title);
        logStart.setBusinessType(0);
        logStart.setMethod(method);
        logStart.setRequestMethod("POST");
        logStart.setOperatorType(1);
        logStart.setOperName(operName);
        logStart.setOperUrl(url);
        logStart.setOperIp("127.0.0.1");
        logStart.setOperLocation("内网ID");
        logStart.setOperParam(param);
        logStart.setJsonResult(result);
        logStart.setStatus(0);
        logStart.setOperTime(DateUtils.getNowDate());
        operLogMapper.insertOperlog(logStart);
    }

    /**
     * 取货校验-按顺序取货
     * 比如到1列1排取货，1列2排有货，则任务暂存，等待1列2排取走后执行
     *
     * @param shelfId
     * @return
     */
    private boolean outShelfCheckInOrder(Integer shelfId) {
        boolean result = true;
        String outShelfNo = "";
        ShelfDomain shelf = shelfDao.getOne(shelfId);
        if (shelf != null) {
            outShelfNo = shelf.getWarehouseNo();
            List<ShelfDomain> list = shelfDao.findOutByConditionAndNoAsc("2", outShelfNo);
            if (list.size() > 0) {
                result = false;
            }
        }
        return result;
    }

    /**
     * 放货校验-按顺序放货
     * 比如到1列2排放货，1列1排无货，则任务暂存，等待1列1排放货后执行
     *
     * @param shelfId
     * @return
     */
    private boolean inShelfCheckInOrder(Integer shelfId) {
        boolean result = true;
        String inShelfNo = "";
        ShelfDomain shelf = shelfDao.getOne(shelfId);
        if (shelf != null) {
            inShelfNo = shelf.getWarehouseNo();
            List<ShelfDomain> list = shelfDao.findInByConditionAndNoAsc("2", inShelfNo);
            if (list.size() > 0) {
                result = false;
            }
        }
        return result;
    }

    /**
     * 放货校验-出库有未完成的任务，不能触发入库
     *
     * @param orderId
     * @return
     */
    private boolean inShelfCheckIfOut(Integer orderId) {
        boolean result = true;
        List<OrderDomain> list = orderDao.findIfOutExists();
        if (list.size() > 0) {
            result = false;
        }
        return result;
    }

    /**
     * 出货校验-入库有未完成的任务，不能触发出库
     *
     * @param orderId
     * @return
     */
    private boolean outShelfCheckIfIn(Integer orderId) {
        boolean result = true;
        List<OrderDomain> list = orderDao.findIfInExists();
        if (list.size() > 0) {
            result = false;
        }
        return result;
    }


    // --------------呼叫物料----------------
    @Data
    public static class XianBianParam {
        String locationName;
    }

    @PostMapping("/setFaZhi")
    @ResponseBody
    public Result setFaZhi(@RequestBody ShelfDomain params) {
        ShelfDomain shelfDomain = shelfDao.findByLocationName("Location-1101");
        shelfDomain.setWmsContent(params.getWmsContent());
        shelfDao.save(shelfDomain);
        return Result.success();
    }

    @PostMapping("/setFaZhiEmpty")
    @ResponseBody
    public Result setFaZhiEmpty(@RequestBody ShelfDomain params) {
        ShelfDomain shelfDomain = shelfDao.findByLocationName("Location-1102");
        shelfDomain.setWmsContent(params.getWmsContent());
        shelfDao.save(shelfDomain);
        return Result.success();
    }

    @PostMapping("/isLowFaZhi")
    @ResponseBody
    public Result isLowFaZhi() {
        List<ShelfDomain> shelfs = shelfDao.findByLoadStatusAndWarehouseNo();
        ShelfDomain shelfDomain = shelfDao.findByLocationName("Location-1101");
        if (shelfs.size() >= Integer.valueOf(shelfDomain.getWmsContent())) {
            return Result.success();
        }
        return Result.error("货架区托盘数【" + shelfs.size() + "】，低于阀值【" + shelfDomain.getWmsContent() + "】");
    }



    @PostMapping("/isLowFaZhiEmpty")
    @ResponseBody
    public Result isLowFaZhiEmpty() {
        List<ShelfDomain> shelfs = shelfDao.findByLoadStatusAndWarehouseNoEmpty();
        ShelfDomain shelfDomain = shelfDao.findByLocationName("Location-1102");
        try {
            Integer.valueOf(shelfDomain.getWmsContent());
        } catch (NumberFormatException e) {
            return Result.error(e.getMessage());
        }
        if (shelfs.size() < Integer.valueOf(shelfDomain.getWmsContent()) || Integer.valueOf(shelfDomain.getWmsContent()) == 0) {
            return Result.success();
        }
        return Result.error("空托盘区托盘数【" + shelfs.size() + "】，达到阀值【" + shelfDomain.getWmsContent() + "】");
    }

    /**
     * 入库、加满
     *
     * @param param
     * @return
     */
    @RequestMapping("/fullShelf")
    @ResponseBody
    Result fullShelf(@RequestBody ShelfDomain param) {
        String locationName = param.getLocationName();
        ShelfDomain sd = shelfDao.findByLocationName(locationName);
        if (sd == null) {
            return Result.error("无该货位");
        }
        if (locationName.contains("12") || locationName.contains("11")) {
            String SN = param.getPalletNo();
            String seqence = param.getShelfType();
            WmsMatList wmsMatList = new WmsMatList();
            wmsMatList.setSn(SN);
            wmsMatList.setStatus("N");
            if (!StringUtils.isEmpty(seqence)) {
                wmsMatList.setBoxes(seqence);
            }
            List<WmsMatList> wmsMatLists = wmsMatListService.selectWmsMatListList(wmsMatList);
            if (wmsMatLists.isEmpty() || StringUtils.isEmpty(SN)) {
                if (!StringUtils.isEmpty(seqence)) {
                    return Result.error("入库失败,该SN或者序列号在物料中不存在");
                }
                return Result.error("入库失败,该SN在物料中不存在");
            }
            sd.setPalletNo(param.getPalletNo());
            sd.setLoadStatus(4);//更改为确认状态4，必须要增加一个确认才能变为2
            sd.setShelfType(seqence);
            shelfDao.save(sd);
            return Result.success();
        }
        sd.setPalletNo(param.getPalletNo());
        sd.setLoadStatus(2);
        sd.setBoxes(param.getBoxes());
        shelfDao.save(sd);
        return Result.success();
    }

    @RequestMapping("/fullConfirmShelf")
    @ResponseBody
    Result fullConfirmShelf(@RequestBody ShelfDomain param) {
        System.out.println(param);
        String locationName = param.getLocationName();
        ShelfDomain sd = shelfDao.findByLocationName(locationName);
        if (sd == null) {
            return Result.error("无该货位");
        }
        if (param.getLoadStatus() != 4) {
            return Result.error("无法人工确认,请确认货位是否已入库");
        }
        sd.setPalletNo(param.getPalletNo());
        sd.setLoadStatus(2);
        sd.setBoxes(param.getBoxes());
        shelfDao.save(sd);
        return Result.success();
    }

    /**
     * 清空
     *
     * @param param
     * @return
     */
    @RequestMapping("/emptyShelf")
    @ResponseBody
    Result emptyShelf(@RequestBody ShelfDomain param) {
        String locationName = param.getLocationName();
        ShelfDomain sd = shelfDao.findByLocationName(locationName);
        if (sd == null) {
            return Result.error("无该货位");
        }
        sd.setPalletNo(null);
        sd.setBoxes(null);
        sd.setShelfType(null);
        sd.setLoadStatus(0);
        shelfDao.save(sd);
        return Result.success();
    }

    /**
     * 清空所有的空托盘区
     *
     * @return
     */
    @RequestMapping("/emptyAllShelf")
    @ResponseBody
    Result emptyAllShelf() {
        try {
            for (int i = 1; i <= 8; i++) {
                ShelfDomain shelf = shelfDao.findByWarehouseNo("C00" + i);
                if (shelf.getLoadStatus() != 0) {
                    shelf.setPalletNo(null);
                    shelf.setBoxes(null);
                    shelf.setLoadStatus(0);
                    shelfDao.save(shelf);
                }
            }
        } catch (Exception e) {
            return Result.error("清空发生异常 " + e.getMessage());
        }
        return Result.success("空托盘区清空成功");
    }
}
